Merge pull request #52999 from khushi8112/pr-52968

refactor: assets module form cleanup (backport #52393)
This commit is contained in:
Khushi Rawat
2026-03-06 15:09:26 +05:30
committed by GitHub
20 changed files with 426 additions and 367 deletions

View File

@@ -97,7 +97,7 @@ def validate_accounting_period_on_doc_save(doc, method=None):
if doc.doctype == "Bank Clearance": if doc.doctype == "Bank Clearance":
return return
elif doc.doctype == "Asset": elif doc.doctype == "Asset":
if doc.is_existing_asset: if doc.asset_type == "Existing Asset":
return return
else: else:
date = doc.available_for_use_date date = doc.available_for_use_date

View File

@@ -534,7 +534,7 @@ cur_frm.fields_dict["select_print_heading"].get_query = function (doc, cdt, cdn)
cur_frm.set_query("wip_composite_asset", "items", function () { cur_frm.set_query("wip_composite_asset", "items", function () {
return { return {
filters: { is_composite_asset: 1, docstatus: 0 }, filters: { asset_type: "Composite Asset", docstatus: 0 },
}; };
}); });

View File

@@ -6,11 +6,11 @@
"docstatus": 0, "docstatus": 0,
"doctype": "Dashboard Chart", "doctype": "Dashboard Chart",
"dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"from_date\":\"frappe.datetime.add_months(frappe.datetime.nowdate(), -12)\",\"to_date\":\"frappe.datetime.nowdate()\"}", "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"from_date\":\"frappe.datetime.add_months(frappe.datetime.nowdate(), -12)\",\"to_date\":\"frappe.datetime.nowdate()\"}",
"filters_json": "{\"status\":\"In Location\",\"group_by\":\"Asset Category\",\"is_existing_asset\":0}", "filters_json": "{\"status\":\"In Location\",\"group_by\":\"Asset Category\",\"asset_type\":[\"!=\",\"Existing Asset\"]}",
"idx": 0, "idx": 0,
"is_public": 1, "is_public": 1,
"is_standard": 1, "is_standard": 1,
"modified": "2020-10-28 23:16:16.939070", "modified": "2026-02-03 15:48:13.407835",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Assets", "module": "Assets",
"name": "Category-wise Asset Value", "name": "Category-wise Asset Value",

View File

@@ -6,11 +6,11 @@
"docstatus": 0, "docstatus": 0,
"doctype": "Dashboard Chart", "doctype": "Dashboard Chart",
"dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"from_date\":\"frappe.datetime.add_months(frappe.datetime.nowdate(), -12)\",\"to_date\":\"frappe.datetime.nowdate()\"}", "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"from_date\":\"frappe.datetime.add_months(frappe.datetime.nowdate(), -12)\",\"to_date\":\"frappe.datetime.nowdate()\"}",
"filters_json": "{\"status\":\"In Location\",\"group_by\":\"Location\",\"is_existing_asset\":0}", "filters_json": "{\"status\":\"In Location\",\"group_by\":\"Location\",\"asset_type\":[\"!=\",\"Existing Asset\"]}",
"idx": 0, "idx": 0,
"is_public": 1, "is_public": 1,
"is_standard": 1, "is_standard": 1,
"modified": "2020-10-28 23:16:07.883312", "modified": "2026-02-03 15:48:13.407835",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Assets", "module": "Assets",
"name": "Location-wise Asset Value", "name": "Location-wise Asset Value",

View File

@@ -100,7 +100,7 @@ def get_charts(fiscal_year, year_start_date, year_end_date):
"company": company, "company": company,
"status": "In Location", "status": "In Location",
"group_by": "Asset Category", "group_by": "Asset Category",
"is_existing_asset": 0, "asset_type": ["!=", "Existing Asset"],
} }
), ),
"type": "Donut", "type": "Donut",
@@ -126,7 +126,12 @@ def get_charts(fiscal_year, year_start_date, year_end_date):
"x_field": "location", "x_field": "location",
"timeseries": 0, "timeseries": 0,
"filters_json": json.dumps( "filters_json": json.dumps(
{"company": company, "status": "In Location", "group_by": "Location", "is_existing_asset": 0} {
"company": company,
"status": "In Location",
"group_by": "Location",
"asset_type": ["!=", "Existing Asset"],
}
), ),
"type": "Donut", "type": "Donut",
"doctype": "Dashboard Chart", "doctype": "Dashboard Chart",

View File

@@ -81,23 +81,79 @@ frappe.ui.form.on("Asset", {
}, },
before_submit: function (frm) { before_submit: function (frm) {
if (frm.doc.is_composite_asset && !frm.has_active_capitalization) { if (frm.doc.asset_type == "Composite Asset" && !frm.has_active_capitalization) {
frappe.throw(__("Please capitalize this asset before submitting.")); frappe.throw(__("Please capitalize this asset before submitting."));
} }
}, },
refresh: function (frm) { refresh: async function (frm) {
frappe.ui.form.trigger("Asset", "is_existing_asset"); frappe.ui.form.trigger("Asset", "asset_type");
frm.toggle_display("next_depreciation_date", frm.doc.docstatus < 1); frm.toggle_display("next_depreciation_date", frm.doc.docstatus < 1);
let has_create_buttons = false;
if (frm.doc.docstatus == 1) { if (frm.doc.docstatus == 1) {
if (["Submitted", "Partially Depreciated"].includes(frm.doc.status)) {
frm.add_custom_button(
__("Asset Value Adjustment"),
function () {
frm.trigger("create_asset_value_adjustment");
},
__("Create")
);
frm.add_custom_button(
__("Asset Repair"),
function () {
frm.trigger("create_asset_repair");
},
__("Create")
);
has_create_buttons = true;
}
if (
!frm.doc.calculate_depreciation &&
["Submitted", "Partially Depreciated", "Fully Depreciated"].includes(frm.doc.status)
) {
frm.add_custom_button(
__("Depreciation Entry"),
function () {
frm.trigger("make_journal_entry");
},
__("Create")
);
has_create_buttons = true;
}
if (has_create_buttons) {
frm.page.set_inner_btn_group_as_primary(__("Create"));
}
if (["Submitted", "Partially Depreciated", "Fully Depreciated"].includes(frm.doc.status)) { if (["Submitted", "Partially Depreciated", "Fully Depreciated"].includes(frm.doc.status)) {
if (frm.doc.maintenance_required && !frm.doc.maintenance_schedule) {
frm.add_custom_button(
__("Maintain Asset"),
function () {
frm.trigger("create_asset_maintenance");
},
__("Actions")
);
}
frm.add_custom_button(
__("Split Asset"),
function () {
frm.trigger("split_asset");
},
__("Actions")
);
frm.add_custom_button( frm.add_custom_button(
__("Transfer Asset"), __("Transfer Asset"),
function () { function () {
erpnext.asset.transfer_asset(frm); erpnext.asset.transfer_asset(frm);
}, },
__("Manage") __("Actions")
); );
frm.add_custom_button( frm.add_custom_button(
@@ -105,7 +161,7 @@ frappe.ui.form.on("Asset", {
function () { function () {
erpnext.asset.scrap_asset(frm); erpnext.asset.scrap_asset(frm);
}, },
__("Manage") __("Actions")
); );
frm.add_custom_button( frm.add_custom_button(
@@ -113,15 +169,7 @@ frappe.ui.form.on("Asset", {
function () { function () {
frm.trigger("sell_asset"); frm.trigger("sell_asset");
}, },
__("Manage") __("Actions")
);
frm.add_custom_button(
__("Split Asset"),
function () {
frm.trigger("split_asset");
},
__("Manage")
); );
} else if (frm.doc.status == "Scrapped") { } else if (frm.doc.status == "Scrapped") {
frm.add_custom_button(__("Restore Asset"), function () { frm.add_custom_button(__("Restore Asset"), function () {
@@ -129,47 +177,9 @@ frappe.ui.form.on("Asset", {
}).addClass("btn-primary"); }).addClass("btn-primary");
} }
if (frm.doc.maintenance_required && !frm.doc.maintenance_schedule) { if (await frm.events.should_show_accounting_ledger(frm)) {
frm.add_custom_button( frm.add_custom_button(
__("Maintain Asset"), __("Accounting Ledger"),
function () {
frm.trigger("create_asset_maintenance");
},
__("Manage")
);
}
if (["Submitted", "Partially Depreciated"].includes(frm.doc.status)) {
frm.add_custom_button(
__("Adjust Asset Value"),
function () {
frm.trigger("create_asset_value_adjustment");
},
__("Manage")
);
frm.add_custom_button(
__("Repair Asset"),
function () {
frm.trigger("create_asset_repair");
},
__("Manage")
);
}
if (!frm.doc.calculate_depreciation) {
frm.add_custom_button(
__("Create Depreciation Entry"),
function () {
frm.trigger("make_journal_entry");
},
__("Manage")
);
}
if (frm.doc.purchase_receipt || !frm.doc.is_existing_asset) {
frm.add_custom_button(
__("View General Ledger"),
function () { function () {
frappe.route_options = { frappe.route_options = {
voucher_no: frm.doc.name, voucher_no: frm.doc.name,
@@ -179,7 +189,7 @@ frappe.ui.form.on("Asset", {
}; };
frappe.set_route("query-report", "General Ledger"); frappe.set_route("query-report", "General Ledger");
}, },
__("Manage") __("View")
); );
} }
@@ -195,7 +205,7 @@ frappe.ui.form.on("Asset", {
if (frm.doc.docstatus == 0) { if (frm.doc.docstatus == 0) {
frm.toggle_reqd("finance_books", frm.doc.calculate_depreciation); frm.toggle_reqd("finance_books", frm.doc.calculate_depreciation);
if (frm.doc.is_composite_asset) { if (frm.doc.asset_type == "Composite Asset") {
frappe.call({ frappe.call({
method: "erpnext.assets.doctype.asset.asset.has_active_capitalization", method: "erpnext.assets.doctype.asset.asset.has_active_capitalization",
args: { args: {
@@ -217,6 +227,28 @@ frappe.ui.form.on("Asset", {
} }
}, },
should_show_accounting_ledger: async function (frm) {
if (["Capitalized"].includes(frm.doc.status)) {
return false;
}
if (
!frm.doc.purchase_receipt &&
!frm.doc.purchase_invoice &&
["Existing Asset", "Composite Component"].includes(frm.doc.asset_type)
) {
return false;
}
const asset_category = await frappe.db.get_value(
"Asset Category",
frm.doc.asset_category,
"enable_cwip_accounting"
);
return !!asset_category.message?.enable_cwip_accounting;
},
set_depr_posting_failure_alert: function (frm) { set_depr_posting_failure_alert: function (frm) {
const alert = ` const alert = `
<div class="row"> <div class="row">
@@ -232,7 +264,8 @@ frappe.ui.form.on("Asset", {
toggle_reference_doc: function (frm) { toggle_reference_doc: function (frm) {
const is_submitted = frm.doc.docstatus === 1; const is_submitted = frm.doc.docstatus === 1;
const is_special_asset = frm.doc.is_existing_asset || frm.doc.is_composite_asset; const is_special_asset =
frm.doc.asset_type == "Existing Asset" || frm.doc.asset_type == "Composite Asset";
const clear_field = (field) => { const clear_field = (field) => {
if (frm.doc[field]) { if (frm.doc[field]) {
@@ -508,18 +541,13 @@ frappe.ui.form.on("Asset", {
}); });
}, },
is_existing_asset: function (frm) { asset_type: function (frm) {
frm.trigger("toggle_reference_doc");
},
is_composite_asset: function (frm) {
if (frm.doc.docstatus == 0) { if (frm.doc.docstatus == 0) {
if (frm.doc.is_composite_asset) { if (frm.doc.asset_type == "Composite Asset") {
frm.set_value("net_purchase_amount", 0); frm.set_value("net_purchase_amount", 0);
} else { } else {
frm.set_df_property("net_purchase_amount", "read_only", 0); frm.set_df_property("net_purchase_amount", "read_only", 0);
} }
frm.trigger("toggle_reference_doc");
} }
}, },

View File

@@ -9,20 +9,17 @@
"engine": "InnoDB", "engine": "InnoDB",
"field_order": [ "field_order": [
"naming_series", "naming_series",
"company",
"item_code", "item_code",
"item_name", "item_name",
"asset_name", "asset_name",
"asset_category",
"location",
"image", "image",
"column_break_3", "column_break_3",
"status", "location",
"company", "asset_category",
"asset_owner", "asset_type",
"asset_owner_company", "maintenance_required",
"is_existing_asset", "calculate_depreciation",
"is_composite_asset",
"is_composite_component",
"purchase_details_section", "purchase_details_section",
"purchase_receipt", "purchase_receipt",
"purchase_receipt_item", "purchase_receipt_item",
@@ -30,31 +27,44 @@
"purchase_invoice_item", "purchase_invoice_item",
"purchase_date", "purchase_date",
"available_for_use_date", "available_for_use_date",
"disposal_date",
"column_break_23", "column_break_23",
"net_purchase_amount", "net_purchase_amount",
"purchase_amount", "purchase_amount",
"asset_quantity", "asset_quantity",
"additional_asset_cost", "additional_asset_cost",
"section_break_uiyd",
"column_break_bbwr",
"column_break_bfkm",
"total_asset_cost", "total_asset_cost",
"disposal_date",
"depreciation_tab", "depreciation_tab",
"calculate_depreciation", "column_break_wqzi",
"column_break_33",
"opening_accumulated_depreciation", "opening_accumulated_depreciation",
"opening_number_of_booked_depreciations",
"is_fully_depreciated", "is_fully_depreciated",
"column_break_33",
"opening_number_of_booked_depreciations",
"section_break_36", "section_break_36",
"finance_books", "finance_books",
"section_break_33", "section_break_33",
"depreciation_method", "depreciation_method",
"value_after_depreciation", "value_after_depreciation",
"total_number_of_depreciations",
"column_break_24",
"frequency_of_depreciation", "frequency_of_depreciation",
"column_break_24",
"next_depreciation_date", "next_depreciation_date",
"total_number_of_depreciations",
"depreciation_schedule_sb", "depreciation_schedule_sb",
"depreciation_schedule_view", "depreciation_schedule_view",
"insurance_details_tab", "other_info_tab",
"accounting_dimensions_section",
"cost_center",
"column_break_rjyw",
"asset_owner_section",
"asset_owner",
"column_break_yeds",
"asset_owner_company",
"customer",
"supplier",
"insurance_section",
"policy_number", "policy_number",
"insurer", "insurer",
"insured_value", "insured_value",
@@ -62,22 +72,17 @@
"insurance_start_date", "insurance_start_date",
"insurance_end_date", "insurance_end_date",
"comprehensive_insurance", "comprehensive_insurance",
"other_info_tab",
"accounting_dimensions_section",
"cost_center",
"section_break_jtou", "section_break_jtou",
"status",
"custodian", "custodian",
"department",
"default_finance_book", "default_finance_book",
"depr_entry_posting_status", "depr_entry_posting_status",
"booked_fixed_asset",
"customer",
"supplier",
"column_break_51", "column_break_51",
"department",
"split_from",
"journal_entry_for_scrap", "journal_entry_for_scrap",
"split_from",
"amended_from", "amended_from",
"maintenance_required", "booked_fixed_asset",
"connections_tab" "connections_tab"
], ],
"fields": [ "fields": [
@@ -106,13 +111,6 @@
"options": "Item", "options": "Item",
"reqd": 1 "reqd": 1
}, },
{
"depends_on": "item_code",
"fetch_from": "item_code.item_name",
"fieldname": "item_name",
"fieldtype": "Read Only",
"label": "Item Name"
},
{ {
"depends_on": "item_code", "depends_on": "item_code",
"fetch_from": "item_code.asset_category", "fetch_from": "item_code.asset_category",
@@ -207,7 +205,7 @@
"fieldname": "purchase_date", "fieldname": "purchase_date",
"fieldtype": "Date", "fieldtype": "Date",
"label": "Purchase Date", "label": "Purchase Date",
"read_only_depends_on": "eval:!doc.is_existing_asset && !doc.is_composite_asset", "read_only_depends_on": "eval:doc.asset_type != \"Existing Asset\" && doc.asset_type != \"Composite Asset\"",
"reqd": 1 "reqd": 1
}, },
{ {
@@ -229,25 +227,18 @@
{ {
"fieldname": "available_for_use_date", "fieldname": "available_for_use_date",
"fieldtype": "Date", "fieldtype": "Date",
"label": "Available-for-use Date", "label": "Available for Use Date",
"mandatory_depends_on": "eval:(!(doc.is_composite_component || doc.is_composite_asset) || doc.docstatus==1)" "mandatory_depends_on": "eval:(!(doc.asset_type == \"Composite Component\" || doc.asset_type == \"Composite Asset\") || doc.docstatus==1)"
}, },
{ {
"default": "0", "default": "0",
"fieldname": "calculate_depreciation", "fieldname": "calculate_depreciation",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Calculate Depreciation", "label": "Calculate Depreciation",
"read_only_depends_on": "eval:(doc.is_composite_asset && !doc.net_purchase_amount) || doc.is_composite_component" "read_only_depends_on": "eval:(doc.asset_type == \"Composite Asset\" && !doc.net_purchase_amount) || doc.asset_type == \"Composite Component\""
}, },
{ {
"default": "0", "depends_on": "eval:(doc.asset_type == \"Existing Asset\")",
"depends_on": "eval:(!doc.is_composite_asset && !doc.is_composite_component)",
"fieldname": "is_existing_asset",
"fieldtype": "Check",
"label": "Is Existing Asset"
},
{
"depends_on": "eval:(doc.is_existing_asset)",
"fieldname": "opening_accumulated_depreciation", "fieldname": "opening_accumulated_depreciation",
"fieldtype": "Currency", "fieldtype": "Currency",
"label": "Opening Accumulated Depreciation", "label": "Opening Accumulated Depreciation",
@@ -257,18 +248,20 @@
"columns": 10, "columns": 10,
"fieldname": "finance_books", "fieldname": "finance_books",
"fieldtype": "Table", "fieldtype": "Table",
"label": "Finance Books",
"options": "Asset Finance Book" "options": "Asset Finance Book"
}, },
{ {
"fieldname": "section_break_33", "fieldname": "section_break_33",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 1 "hidden": 1,
"label": "Depreciation Details"
}, },
{ {
"fieldname": "depreciation_method", "fieldname": "depreciation_method",
"fieldtype": "Select", "fieldtype": "Select",
"label": "Depreciation Method", "label": "Depreciation Method",
"options": "\nStraight Line\nDouble Declining Balance\nManual" "options": "\nStraight Line\nDouble Declining Balance\nWritten Down Value\nManual"
}, },
{ {
"fieldname": "value_after_depreciation", "fieldname": "value_after_depreciation",
@@ -295,6 +288,7 @@
{ {
"fieldname": "next_depreciation_date", "fieldname": "next_depreciation_date",
"fieldtype": "Date", "fieldtype": "Date",
"hidden": 1,
"label": "Next Depreciation Date", "label": "Next Depreciation Date",
"no_copy": 1 "no_copy": 1
}, },
@@ -364,7 +358,7 @@
"fieldtype": "Column Break" "fieldtype": "Column Break"
}, },
{ {
"depends_on": "eval:!doc.is_composite_asset && !doc.is_existing_asset", "depends_on": "eval:doc.asset_type != \"Composite Asset\" && doc.asset_type != \"Existing Asset\"",
"fieldname": "purchase_receipt", "fieldname": "purchase_receipt",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Purchase Receipt", "label": "Purchase Receipt",
@@ -373,7 +367,7 @@
"print_hide": 1 "print_hide": 1
}, },
{ {
"depends_on": "eval:!doc.is_composite_asset && !doc.is_existing_asset", "depends_on": "eval:doc.asset_type != \"Composite Asset\" && doc.asset_type != \"Existing Asset\"",
"fieldname": "purchase_invoice", "fieldname": "purchase_invoice",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Purchase Invoice", "label": "Purchase Invoice",
@@ -399,7 +393,7 @@
"read_only": 1 "read_only": 1
}, },
{ {
"collapsible_depends_on": "is_existing_asset", "collapsible_depends_on": "eval:doc.asset_type == \"Existing Asset\"",
"fieldname": "purchase_details_section", "fieldname": "purchase_details_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Purchase Details" "label": "Purchase Details"
@@ -413,10 +407,9 @@
"fieldtype": "Column Break" "fieldtype": "Column Break"
}, },
{ {
"depends_on": "calculate_depreciation", "depends_on": "eval: doc.calculate_depreciation",
"fieldname": "section_break_36", "fieldname": "section_break_36",
"fieldtype": "Section Break", "fieldtype": "Section Break"
"label": "Finance Books"
}, },
{ {
"fieldname": "split_from", "fieldname": "split_from",
@@ -455,18 +448,11 @@
}, },
{ {
"default": "0", "default": "0",
"depends_on": "eval:(doc.is_existing_asset)",
"fieldname": "is_fully_depreciated", "fieldname": "is_fully_depreciated",
"fieldtype": "Check", "fieldtype": "Check",
"hidden": 1,
"label": "Is Fully Depreciated" "label": "Is Fully Depreciated"
}, },
{
"default": "0",
"depends_on": "eval:(!doc.is_existing_asset && !doc.is_composite_component)",
"fieldname": "is_composite_asset",
"fieldtype": "Check",
"label": "Is Composite Asset"
},
{ {
"depends_on": "eval:doc.docstatus > 0", "depends_on": "eval:doc.docstatus > 0",
"fieldname": "total_asset_cost", "fieldname": "total_asset_cost",
@@ -496,7 +482,7 @@
"read_only": 1 "read_only": 1
}, },
{ {
"depends_on": "eval:(doc.is_existing_asset)", "depends_on": "eval:(doc.asset_type == \"Existing Asset\")",
"fieldname": "opening_number_of_booked_depreciations", "fieldname": "opening_number_of_booked_depreciations",
"fieldtype": "Int", "fieldtype": "Int",
"label": "Opening Number of Booked Depreciations" "label": "Opening Number of Booked Depreciations"
@@ -513,15 +499,10 @@
"hidden": 1, "hidden": 1,
"label": "Purchase Invoice Item" "label": "Purchase Invoice Item"
}, },
{
"fieldname": "insurance_details_tab",
"fieldtype": "Tab Break",
"label": "Insurance"
},
{ {
"fieldname": "other_info_tab", "fieldname": "other_info_tab",
"fieldtype": "Tab Break", "fieldtype": "Tab Break",
"label": "Other Info" "label": "More Info"
}, },
{ {
"fieldname": "connections_tab", "fieldname": "connections_tab",
@@ -530,6 +511,7 @@
"show_dashboard": 1 "show_dashboard": 1
}, },
{ {
"depends_on": "eval: doc.calculate_depreciation || doc.asset_type == \"Existing Asset\"",
"fieldname": "depreciation_tab", "fieldname": "depreciation_tab",
"fieldtype": "Tab Break", "fieldtype": "Tab Break",
"label": "Depreciation" "label": "Depreciation"
@@ -544,20 +526,61 @@
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Additional Info" "label": "Additional Info"
}, },
{
"default": "0",
"depends_on": "eval:(!doc.is_existing_asset && !doc.is_composite_asset)",
"fieldname": "is_composite_component",
"fieldtype": "Check",
"label": "Is Composite Component"
},
{ {
"fieldname": "net_purchase_amount", "fieldname": "net_purchase_amount",
"fieldtype": "Currency", "fieldtype": "Currency",
"label": "Net Purchase Amount", "label": "Net Purchase Amount",
"mandatory_depends_on": "eval:(!doc.is_composite_asset || doc.docstatus==1)", "mandatory_depends_on": "eval:(doc.asset_type != \"Composite Asset\" || doc.docstatus==1)",
"options": "Company:company:default_currency", "options": "Company:company:default_currency",
"read_only_depends_on": "eval: doc.is_composite_asset" "read_only_depends_on": "eval: doc.asset_type == \"Composite Asset\""
},
{
"fieldname": "asset_type",
"fieldtype": "Select",
"label": "Asset Type",
"options": "\nExisting Asset\nComposite Asset\nComposite Component"
},
{
"fieldname": "column_break_wqzi",
"fieldtype": "Column Break"
},
{
"fieldname": "column_break_rjyw",
"fieldtype": "Column Break"
},
{
"fieldname": "insurance_section",
"fieldtype": "Section Break",
"label": "Insurance"
},
{
"fieldname": "section_break_uiyd",
"fieldtype": "Section Break"
},
{
"fieldname": "column_break_bbwr",
"fieldtype": "Column Break"
},
{
"fieldname": "column_break_bfkm",
"fieldtype": "Column Break"
},
{
"fetch_from": "item_code.item_name",
"fetch_if_empty": 1,
"fieldname": "item_name",
"fieldtype": "Read Only",
"hidden": 1,
"label": "Item Name"
},
{
"fieldname": "asset_owner_section",
"fieldtype": "Section Break",
"label": "Ownership"
},
{
"fieldname": "column_break_yeds",
"fieldtype": "Column Break"
} }
], ],
"idx": 72, "idx": 72,
@@ -601,7 +624,7 @@
"link_fieldname": "target_asset" "link_fieldname": "target_asset"
} }
], ],
"modified": "2025-12-18 16:36:40.904246", "modified": "2026-02-05 12:42:45.350216",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Assets", "module": "Assets",
"name": "Asset", "name": "Asset",

View File

@@ -56,6 +56,7 @@ class Asset(AccountsController):
asset_owner: DF.Literal["", "Company", "Supplier", "Customer"] asset_owner: DF.Literal["", "Company", "Supplier", "Customer"]
asset_owner_company: DF.Link | None asset_owner_company: DF.Link | None
asset_quantity: DF.Int asset_quantity: DF.Int
asset_type: DF.Literal["", "Existing Asset", "Composite Asset", "Composite Component"]
available_for_use_date: DF.Date | None available_for_use_date: DF.Date | None
booked_fixed_asset: DF.Check booked_fixed_asset: DF.Check
calculate_depreciation: DF.Check calculate_depreciation: DF.Check
@@ -67,7 +68,9 @@ class Asset(AccountsController):
default_finance_book: DF.Link | None default_finance_book: DF.Link | None
department: DF.Link | None department: DF.Link | None
depr_entry_posting_status: DF.Literal["", "Successful", "Failed"] depr_entry_posting_status: DF.Literal["", "Successful", "Failed"]
depreciation_method: DF.Literal["", "Straight Line", "Double Declining Balance", "Manual"] depreciation_method: DF.Literal[
"", "Straight Line", "Double Declining Balance", "Written Down Value", "Manual"
]
disposal_date: DF.Date | None disposal_date: DF.Date | None
finance_books: DF.Table[AssetFinanceBook] finance_books: DF.Table[AssetFinanceBook]
frequency_of_depreciation: DF.Int frequency_of_depreciation: DF.Int
@@ -76,9 +79,6 @@ class Asset(AccountsController):
insurance_start_date: DF.Date | None insurance_start_date: DF.Date | None
insured_value: DF.Data | None insured_value: DF.Data | None
insurer: DF.Data | None insurer: DF.Data | None
is_composite_asset: DF.Check
is_composite_component: DF.Check
is_existing_asset: DF.Check
is_fully_depreciated: DF.Check is_fully_depreciated: DF.Check
item_code: DF.Link item_code: DF.Link
item_name: DF.ReadOnly | None item_name: DF.ReadOnly | None
@@ -243,7 +243,7 @@ class Asset(AccountsController):
self.set_total_booked_depreciations() self.set_total_booked_depreciations()
def before_submit(self): def before_submit(self):
if self.is_composite_asset and not has_active_capitalization(self.name): if self.asset_type == "Composite Asset" and not has_active_capitalization(self.name):
if self.split_from and has_active_capitalization(self.split_from): if self.split_from and has_active_capitalization(self.split_from):
return return
frappe.throw(_("Please capitalize this asset before submitting.")) frappe.throw(_("Please capitalize this asset before submitting."))
@@ -252,7 +252,11 @@ class Asset(AccountsController):
self.validate_in_use_date() self.validate_in_use_date()
self.make_asset_movement() self.make_asset_movement()
self.reload() self.reload()
if not self.booked_fixed_asset and not self.is_composite_component and self.validate_make_gl_entry(): if (
not self.booked_fixed_asset
and self.asset_type != "Composite Component"
and self.validate_make_gl_entry()
):
self.make_gl_entries() self.make_gl_entries()
if self.calculate_depreciation and not self.split_from: if self.calculate_depreciation and not self.split_from:
convert_draft_asset_depr_schedules_into_active(self) convert_draft_asset_depr_schedules_into_active(self)
@@ -267,7 +271,7 @@ class Asset(AccountsController):
cancel_asset_depr_schedules(self) cancel_asset_depr_schedules(self)
self.set_status() self.set_status()
self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry") self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry")
if not self.is_composite_component: if self.asset_type != "Composite Component":
make_reverse_gl_entries(voucher_type="Asset", voucher_no=self.name) make_reverse_gl_entries(voucher_type="Asset", voucher_no=self.name)
self.db_set("booked_fixed_asset", 0) self.db_set("booked_fixed_asset", 0)
add_asset_activity(self.name, _("Asset cancelled")) add_asset_activity(self.name, _("Asset cancelled"))
@@ -285,7 +289,7 @@ class Asset(AccountsController):
add_asset_activity(self.name, _("Asset deleted")) add_asset_activity(self.name, _("Asset deleted"))
def set_purchase_doc_row_item(self): def set_purchase_doc_row_item(self):
if self.is_existing_asset or self.is_composite_asset: if self.asset_type == "Existing Asset" or self.asset_type == "Composite Asset":
return return
self.purchase_amount = self.net_purchase_amount self.purchase_amount = self.net_purchase_amount
@@ -328,7 +332,7 @@ class Asset(AccountsController):
) )
) )
if self.is_existing_asset and self.purchase_invoice: if self.asset_type == "Existing Asset" and self.purchase_invoice:
frappe.throw(_("Purchase Invoice cannot be made against an existing asset {0}").format(self.name)) frappe.throw(_("Purchase Invoice cannot be made against an existing asset {0}").format(self.name))
def validate_item(self): def validate_item(self):
@@ -374,7 +378,7 @@ class Asset(AccountsController):
) )
def validate_in_use_date(self): def validate_in_use_date(self):
if not self.available_for_use_date and not self.is_composite_component: if not self.available_for_use_date and self.asset_type != "Composite Component":
frappe.throw(_("Available for use date is required")) frappe.throw(_("Available for use date is required"))
for d in self.finance_books: for d in self.finance_books:
@@ -442,13 +446,13 @@ class Asset(AccountsController):
if not self.asset_category: if not self.asset_category:
self.asset_category = frappe.get_cached_value("Item", self.item_code, "asset_category") self.asset_category = frappe.get_cached_value("Item", self.item_code, "asset_category")
if not flt(self.net_purchase_amount) and not self.is_composite_asset: if not flt(self.net_purchase_amount) and self.asset_type != "Composite Asset":
frappe.throw(_("Net Purchase Amount is mandatory"), frappe.MandatoryError) frappe.throw(_("Net Purchase Amount is mandatory"), frappe.MandatoryError)
if is_cwip_accounting_enabled(self.asset_category): if is_cwip_accounting_enabled(self.asset_category):
if ( if (
not self.is_existing_asset not self.asset_type == "Existing Asset"
and not self.is_composite_asset and not self.asset_type == "Composite Asset"
and not self.purchase_receipt and not self.purchase_receipt
and not self.purchase_invoice and not self.purchase_invoice
): ):
@@ -477,7 +481,7 @@ class Asset(AccountsController):
if self.is_fully_depreciated: if self.is_fully_depreciated:
frappe.throw(_("Depreciation cannot be calculated for fully depreciated assets")) frappe.throw(_("Depreciation cannot be calculated for fully depreciated assets"))
if self.is_existing_asset: if self.asset_type == "Existing Asset":
return return
if self.available_for_use_date and getdate(self.available_for_use_date) < getdate(self.purchase_date): if self.available_for_use_date and getdate(self.available_for_use_date) < getdate(self.purchase_date):
@@ -549,7 +553,7 @@ class Asset(AccountsController):
) )
def validate_gross_and_purchase_amount(self): def validate_gross_and_purchase_amount(self):
if self.is_existing_asset: if self.asset_type == "Existing Asset":
return return
if self.net_purchase_amount and self.net_purchase_amount != self.purchase_amount: if self.net_purchase_amount and self.net_purchase_amount != self.purchase_amount:
@@ -617,7 +621,7 @@ class Asset(AccountsController):
self.validate_depreciation_start_date(row) self.validate_depreciation_start_date(row)
self.validate_total_number_of_depreciations_and_frequency(row) self.validate_total_number_of_depreciations_and_frequency(row)
if not self.is_existing_asset: if self.asset_type != "Existing Asset":
self.opening_accumulated_depreciation = 0 self.opening_accumulated_depreciation = 0
self.opening_number_of_booked_depreciations = 0 self.opening_number_of_booked_depreciations = 0
else: else:
@@ -770,7 +774,7 @@ class Asset(AccountsController):
def get_status(self): def get_status(self):
"""Returns status based on whether it is draft, submitted, scrapped or depreciated""" """Returns status based on whether it is draft, submitted, scrapped or depreciated"""
if self.docstatus == 0: if self.docstatus == 0:
if self.is_composite_asset: if self.asset_type == "Composite Asset":
status = "Work In Progress" status = "Work In Progress"
else: else:
status = "Draft" status = "Draft"
@@ -843,7 +847,7 @@ class Asset(AccountsController):
return records return records
def validate_make_gl_entry(self): def validate_make_gl_entry(self):
if self.is_composite_asset: if self.asset_type == "Composite Asset":
return True return True
purchase_document = self.get_purchase_document() purchase_document = self.get_purchase_document()
@@ -924,7 +928,7 @@ class Asset(AccountsController):
purchase_document = self.get_purchase_document() purchase_document = self.get_purchase_document()
fixed_asset_account, cwip_account = self.get_fixed_asset_account(), self.get_cwip_account() fixed_asset_account, cwip_account = self.get_fixed_asset_account(), self.get_cwip_account()
if (self.is_composite_asset or (purchase_document and self.purchase_amount)) and getdate( if (self.asset_type == "Composite Asset" or (purchase_document and self.purchase_amount)) and getdate(
self.available_for_use_date self.available_for_use_date
) <= getdate(): ) <= getdate():
gl_entries.append( gl_entries.append(
@@ -964,7 +968,7 @@ class Asset(AccountsController):
self.db_set("booked_fixed_asset", 1) self.db_set("booked_fixed_asset", 1)
def check_asset_capitalization_gl_entries(self): def check_asset_capitalization_gl_entries(self):
if self.is_composite_asset: if self.asset_type == "Composite Asset":
result = frappe.db.get_value( result = frappe.db.get_value(
"Asset Capitalization", "Asset Capitalization",
{"target_asset": self.name, "docstatus": 1}, {"target_asset": self.name, "docstatus": 1},
@@ -1395,7 +1399,7 @@ def process_asset_split(existing_asset, split_qty, splitted_asset=None, is_new_a
def set_split_asset_values(asset_doc, scaling_factor, split_qty, existing_asset, is_new_asset): def set_split_asset_values(asset_doc, scaling_factor, split_qty, existing_asset, is_new_asset):
asset_doc.net_purchase_amount = existing_asset.net_purchase_amount * scaling_factor asset_doc.net_purchase_amount = existing_asset.net_purchase_amount * scaling_factor
asset_doc.purchase_amount = existing_asset.net_purchase_amount asset_doc.purchase_amount = existing_asset.net_purchase_amount * scaling_factor
asset_doc.additional_asset_cost = existing_asset.additional_asset_cost * scaling_factor asset_doc.additional_asset_cost = existing_asset.additional_asset_cost * scaling_factor
asset_doc.total_asset_cost = asset_doc.net_purchase_amount + asset_doc.additional_asset_cost asset_doc.total_asset_cost = asset_doc.net_purchase_amount + asset_doc.additional_asset_cost
asset_doc.opening_accumulated_depreciation = ( asset_doc.opening_accumulated_depreciation = (

View File

@@ -786,10 +786,14 @@ def get_disposal_account_and_cost_center(company):
@frappe.whitelist() @frappe.whitelist()
def get_value_after_depreciation_on_disposal_date(asset, disposal_date, finance_book=None): def get_value_after_depreciation_on_disposal_date(
asset: str,
disposal_date: str,
finance_book: str | None = None,
) -> float:
asset_doc = frappe.get_doc("Asset", asset) asset_doc = frappe.get_doc("Asset", asset)
if asset_doc.is_composite_component: if asset_doc.asset_type == "Composite Component":
validate_disposal_date(asset_doc.purchase_date, getdate(disposal_date), "purchase") validate_disposal_date(asset_doc.purchase_date, getdate(disposal_date), "purchase")
return flt(asset_doc.value_after_depreciation) return flt(asset_doc.value_after_depreciation)

View File

@@ -71,16 +71,16 @@ class TestAsset(AssetSetup):
self.assertRaises(frappe.MandatoryError, asset.save) self.assertRaises(frappe.MandatoryError, asset.save)
def test_pr_or_pi_mandatory_if_not_existing_asset(self): def test_pr_or_pi_mandatory_if_not_existing_asset(self):
"""Tests if either PI or PR is present if CWIP is enabled and is_existing_asset=0.""" """Tests if either PI or PR is present if CWIP is enabled and asset_type == Existing Asset."""
asset = create_asset(item_code="Macbook Pro", do_not_save=1) asset = create_asset(item_code="Macbook Pro", do_not_save=1)
asset.is_existing_asset = 0 asset.asset_type = ""
self.assertRaises(frappe.ValidationError, asset.save) self.assertRaises(frappe.ValidationError, asset.save)
def test_available_for_use_date_is_after_purchase_date(self): def test_available_for_use_date_is_after_purchase_date(self):
asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, do_not_save=1) asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, do_not_save=1)
asset.is_existing_asset = 0 asset.asset_type = ""
asset.purchase_date = getdate("2021-10-10") asset.purchase_date = getdate("2021-10-10")
asset.available_for_use_date = getdate("2021-10-1") asset.available_for_use_date = getdate("2021-10-1")
@@ -183,7 +183,7 @@ class TestAsset(AssetSetup):
asset.submit() asset.submit()
def test_is_fixed_asset_set(self): def test_is_fixed_asset_set(self):
asset = create_asset(is_existing_asset=1) asset = create_asset(asset_type="Existing Asset")
doc = frappe.new_doc("Purchase Invoice") doc = frappe.new_doc("Purchase Invoice")
doc.company = "_Test Company" doc.company = "_Test Company"
doc.supplier = "_Test Supplier" doc.supplier = "_Test Supplier"
@@ -710,7 +710,7 @@ class TestAsset(AssetSetup):
# create an asset # create an asset
asset = create_asset( asset = create_asset(
item_code="Macbook Pro", item_code="Macbook Pro",
is_existing_asset=1, asset_type="Existing Asset",
calculate_depreciation=1, calculate_depreciation=1,
available_for_use_date=purchase_date, available_for_use_date=purchase_date,
purchase_date=purchase_date, purchase_date=purchase_date,
@@ -890,7 +890,7 @@ class TestDepreciationMethods(AssetSetup):
asset = create_asset( asset = create_asset(
calculate_depreciation=1, calculate_depreciation=1,
available_for_use_date="2030-06-06", available_for_use_date="2030-06-06",
is_existing_asset=1, asset_type="Existing Asset",
opening_number_of_booked_depreciations=2, opening_number_of_booked_depreciations=2,
opening_accumulated_depreciation=47178.08, opening_accumulated_depreciation=47178.08,
expected_value_after_useful_life=10000, expected_value_after_useful_life=10000,
@@ -939,7 +939,7 @@ class TestDepreciationMethods(AssetSetup):
asset = create_asset( asset = create_asset(
calculate_depreciation=1, calculate_depreciation=1,
available_for_use_date="2030-01-01", available_for_use_date="2030-01-01",
is_existing_asset=1, asset_type="Existing Asset",
depreciation_method="Double Declining Balance", depreciation_method="Double Declining Balance",
opening_number_of_booked_depreciations=1, opening_number_of_booked_depreciations=1,
opening_accumulated_depreciation=50000, opening_accumulated_depreciation=50000,
@@ -1680,7 +1680,7 @@ class TestDepreciationBasics(AssetSetup):
self.assertEqual(asset.finance_books[0].value_after_depreciation, 100000.0) self.assertEqual(asset.finance_books[0].value_after_depreciation, 100000.0)
def test_asset_cost_center(self): def test_asset_cost_center(self):
asset = create_asset(is_existing_asset=1, do_not_save=1) asset = create_asset(asset_type="Existing Asset", do_not_save=1)
asset.cost_center = "Main - WP" asset.cost_center = "Main - WP"
self.assertRaises(frappe.ValidationError, asset.submit) self.assertRaises(frappe.ValidationError, asset.submit)
@@ -1717,7 +1717,7 @@ class TestDepreciationBasics(AssetSetup):
def test_manual_depreciation_for_existing_asset(self): def test_manual_depreciation_for_existing_asset(self):
asset = create_asset( asset = create_asset(
item_code="Macbook Pro", item_code="Macbook Pro",
is_existing_asset=1, asset_type="Existing Asset",
purchase_date="2020-01-30", purchase_date="2020-01-30",
available_for_use_date="2020-01-30", available_for_use_date="2020-01-30",
submit=1, submit=1,
@@ -1843,7 +1843,7 @@ class TestDepreciationBasics(AssetSetup):
# Create composite asset # Create composite asset
wip_composite_asset = create_asset( wip_composite_asset = create_asset(
asset_name="Asset Capitalization WIP Composite Asset for Split", asset_name="Asset Capitalization WIP Composite Asset for Split",
is_composite_asset=1, asset_type="Composite Asset",
warehouse="Stores - TCP1", warehouse="Stores - TCP1",
company=company, company=company,
asset_quantity=2, # Set quantity > 1 to allow splitting asset_quantity=2, # Set quantity > 1 to allow splitting
@@ -1937,9 +1937,7 @@ def create_asset(**args):
"available_for_use_date": args.available_for_use_date or "2020-06-06", "available_for_use_date": args.available_for_use_date or "2020-06-06",
"location": args.location or "Test Location", "location": args.location or "Test Location",
"asset_owner": args.asset_owner or "Company", "asset_owner": args.asset_owner or "Company",
"is_existing_asset": args.is_existing_asset or 1, "asset_type": args.asset_type or "Existing Asset",
"is_composite_asset": args.is_composite_asset or 0,
"is_composite_component": args.is_composite_component or 0,
"asset_quantity": args.get("asset_quantity") or 1, "asset_quantity": args.get("asset_quantity") or 1,
"depr_entry_posting_status": args.depr_entry_posting_status or "", "depr_entry_posting_status": args.depr_entry_posting_status or "",
} }
@@ -1961,7 +1959,7 @@ def create_asset(**args):
}, },
) )
if asset.is_composite_asset: if asset.asset_type == "Composite Asset":
asset.net_purchase_amount = 0 asset.net_purchase_amount = 0
asset.purchase_amount = 0 asset.purchase_amount = 0

View File

@@ -18,10 +18,7 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s
this.show_general_ledger(); this.show_general_ledger();
erpnext.toggle_serial_batch_fields(this.frm); erpnext.toggle_serial_batch_fields(this.frm);
if ( if (this.frm.doc.stock_items && this.frm.doc.stock_items.length) {
(this.frm.doc.stock_items && this.frm.doc.stock_items.length) ||
!this.frm.doc.target_is_fixed_asset
) {
this.show_stock_ledger(); this.show_stock_ledger();
} }
@@ -42,7 +39,7 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s
me.frm.set_query("target_asset", function () { me.frm.set_query("target_asset", function () {
return { return {
filters: { is_composite_asset: 1, docstatus: 0 }, filters: { asset_type: "Composite Asset", docstatus: 0 },
}; };
}); });
@@ -241,10 +238,6 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s
this.calculate_totals(); this.calculate_totals();
} }
target_qty() {
this.calculate_totals();
}
rate() { rate() {
this.calculate_totals(); this.calculate_totals();
} }
@@ -486,10 +479,7 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s
me.frm.doc.stock_items_total + me.frm.doc.asset_items_total + me.frm.doc.service_items_total; me.frm.doc.stock_items_total + me.frm.doc.asset_items_total + me.frm.doc.service_items_total;
me.frm.doc.total_value = flt(me.frm.doc.total_value, precision("total_value")); me.frm.doc.total_value = flt(me.frm.doc.total_value, precision("total_value"));
me.frm.doc.target_qty = flt(me.frm.doc.target_qty, precision("target_qty")); me.frm.doc.target_incoming_rate = me.frm.doc.total_value;
me.frm.doc.target_incoming_rate = me.frm.doc.target_qty
? me.frm.doc.total_value / flt(me.frm.doc.target_qty)
: me.frm.doc.total_value;
me.frm.refresh_fields(); me.frm.refresh_fields();
} }

View File

@@ -9,30 +9,33 @@
"field_order": [ "field_order": [
"title", "title",
"naming_series", "naming_series",
"company",
"target_asset", "target_asset",
"target_asset_name", "target_asset_name",
"target_item_code",
"finance_book",
"target_qty",
"column_break_9", "column_break_9",
"company", "finance_book",
"posting_date", "posting_date",
"posting_time", "posting_time",
"set_posting_time", "set_posting_time",
"target_batch_no", "target_item_code",
"target_serial_no",
"amended_from", "amended_from",
"target_is_fixed_asset",
"target_has_batch_no",
"target_has_serial_no",
"section_break_16", "section_break_16",
"stock_items", "stock_items",
"section_break_urtz",
"column_break_gqep",
"column_break_yvlx",
"stock_items_total", "stock_items_total",
"section_break_26", "section_break_26",
"asset_items", "asset_items",
"section_break_arbh",
"column_break_boeu",
"column_break_qecy",
"asset_items_total", "asset_items_total",
"service_expenses_section", "service_expenses_section",
"service_items", "service_items",
"section_break_ptna",
"column_break_szvh",
"column_break_katv",
"service_items_total", "service_items_total",
"totals_section", "totals_section",
"total_value", "total_value",
@@ -55,20 +58,12 @@
"depends_on": "eval:(doc.target_item_code && !doc.__islocal)", "depends_on": "eval:(doc.target_item_code && !doc.__islocal)",
"fieldname": "target_item_code", "fieldname": "target_item_code",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 1,
"in_standard_filter": 1, "in_standard_filter": 1,
"label": "Target Item Code", "label": "Target Item Code",
"options": "Item", "options": "Item",
"read_only": 1 "read_only": 1
}, },
{
"default": "0",
"fetch_from": "target_item_code.is_fixed_asset",
"fieldname": "target_is_fixed_asset",
"fieldtype": "Check",
"hidden": 1,
"label": "Target Is Fixed Asset",
"read_only": 1
},
{ {
"fieldname": "target_asset", "fieldname": "target_asset",
"fieldtype": "Link", "fieldtype": "Link",
@@ -143,6 +138,7 @@
"depends_on": "eval:doc.docstatus == 0 || (doc.stock_items && doc.stock_items.length)", "depends_on": "eval:doc.docstatus == 0 || (doc.stock_items && doc.stock_items.length)",
"fieldname": "section_break_16", "fieldname": "section_break_16",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hide_border": 1,
"label": "Consumed Stock Items" "label": "Consumed Stock Items"
}, },
{ {
@@ -151,49 +147,11 @@
"label": "Stock Items", "label": "Stock Items",
"options": "Asset Capitalization Stock Item" "options": "Asset Capitalization Stock Item"
}, },
{
"depends_on": "target_has_batch_no",
"fieldname": "target_batch_no",
"fieldtype": "Link",
"label": "Target Batch No",
"options": "Batch"
},
{
"default": "1",
"fieldname": "target_qty",
"fieldtype": "Float",
"hidden": 1,
"label": "Target Qty",
"read_only": 1
},
{
"default": "0",
"fetch_from": "target_item_code.has_batch_no",
"fieldname": "target_has_batch_no",
"fieldtype": "Check",
"hidden": 1,
"label": "Target Has Batch No",
"read_only": 1
},
{
"default": "0",
"fetch_from": "target_item_code.has_serial_no",
"fieldname": "target_has_serial_no",
"fieldtype": "Check",
"hidden": 1,
"label": "Target Has Serial No",
"read_only": 1
},
{
"depends_on": "target_has_serial_no",
"fieldname": "target_serial_no",
"fieldtype": "Small Text",
"label": "Target Serial No"
},
{ {
"depends_on": "eval:doc.docstatus == 0 || (doc.asset_items && doc.asset_items.length)", "depends_on": "eval:doc.docstatus == 0 || (doc.asset_items && doc.asset_items.length)",
"fieldname": "section_break_26", "fieldname": "section_break_26",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hide_border": 1,
"label": "Consumed Assets" "label": "Consumed Assets"
}, },
{ {
@@ -203,6 +161,7 @@
"options": "Asset Capitalization Asset Item" "options": "Asset Capitalization Asset Item"
}, },
{ {
"depends_on": "eval: doc.stock_items_total",
"fieldname": "stock_items_total", "fieldname": "stock_items_total",
"fieldtype": "Currency", "fieldtype": "Currency",
"label": "Consumed Stock Total Value", "label": "Consumed Stock Total Value",
@@ -210,6 +169,7 @@
"read_only": 1 "read_only": 1
}, },
{ {
"depends_on": "eval: doc.asset_items_total",
"fieldname": "asset_items_total", "fieldname": "asset_items_total",
"fieldtype": "Currency", "fieldtype": "Currency",
"label": "Consumed Asset Total Value", "label": "Consumed Asset Total Value",
@@ -226,6 +186,7 @@
"depends_on": "eval:doc.docstatus == 0 || (doc.service_items && doc.service_items.length)", "depends_on": "eval:doc.docstatus == 0 || (doc.service_items && doc.service_items.length)",
"fieldname": "service_expenses_section", "fieldname": "service_expenses_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hide_border": 1,
"label": "Service Expenses" "label": "Service Expenses"
}, },
{ {
@@ -235,6 +196,7 @@
"options": "Asset Capitalization Service Item" "options": "Asset Capitalization Service Item"
}, },
{ {
"depends_on": "eval: doc.service_items_total",
"fieldname": "service_items_total", "fieldname": "service_items_total",
"fieldtype": "Currency", "fieldtype": "Currency",
"label": "Service Expense Total Amount", "label": "Service Expense Total Amount",
@@ -277,10 +239,10 @@
"options": "Cost Center" "options": "Cost Center"
}, },
{ {
"fieldname": "project", "fieldname": "project",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Project", "label": "Project",
"options": "Project" "options": "Project"
}, },
{ {
"fieldname": "dimension_col_break", "fieldname": "dimension_col_break",
@@ -292,12 +254,48 @@
"label": "Target Fixed Asset Account", "label": "Target Fixed Asset Account",
"options": "Account", "options": "Account",
"read_only": 1 "read_only": 1
},
{
"fieldname": "section_break_urtz",
"fieldtype": "Section Break"
},
{
"fieldname": "column_break_gqep",
"fieldtype": "Column Break"
},
{
"fieldname": "column_break_yvlx",
"fieldtype": "Column Break"
},
{
"fieldname": "section_break_arbh",
"fieldtype": "Section Break"
},
{
"fieldname": "column_break_boeu",
"fieldtype": "Column Break"
},
{
"fieldname": "column_break_qecy",
"fieldtype": "Column Break"
},
{
"fieldname": "section_break_ptna",
"fieldtype": "Section Break"
},
{
"fieldname": "column_break_szvh",
"fieldtype": "Column Break"
},
{
"fieldname": "column_break_katv",
"fieldtype": "Column Break"
} }
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2025-05-20 15:15:12.110035", "modified": "2026-02-06 01:52:41.890713",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Assets", "module": "Assets",
"name": "Asset Capitalization", "name": "Asset Capitalization",

View File

@@ -39,9 +39,6 @@ force_fields = [
"target_asset_name", "target_asset_name",
"item_name", "item_name",
"asset_name", "asset_name",
"target_is_fixed_asset",
"target_has_serial_no",
"target_has_batch_no",
"stock_uom", "stock_uom",
"fixed_asset_account", "fixed_asset_account",
"valuation_rate", "valuation_rate",
@@ -76,6 +73,7 @@ class AssetCapitalization(StockController):
naming_series: DF.Literal["ACC-ASC-.YYYY.-"] naming_series: DF.Literal["ACC-ASC-.YYYY.-"]
posting_date: DF.Date posting_date: DF.Date
posting_time: DF.Time posting_time: DF.Time
project: DF.Link | None
service_items: DF.Table[AssetCapitalizationServiceItem] service_items: DF.Table[AssetCapitalizationServiceItem]
service_items_total: DF.Currency service_items_total: DF.Currency
set_posting_time: DF.Check set_posting_time: DF.Check
@@ -83,15 +81,9 @@ class AssetCapitalization(StockController):
stock_items_total: DF.Currency stock_items_total: DF.Currency
target_asset: DF.Link | None target_asset: DF.Link | None
target_asset_name: DF.Data | None target_asset_name: DF.Data | None
target_batch_no: DF.Link | None
target_fixed_asset_account: DF.Link | None target_fixed_asset_account: DF.Link | None
target_has_batch_no: DF.Check
target_has_serial_no: DF.Check
target_incoming_rate: DF.Currency target_incoming_rate: DF.Currency
target_is_fixed_asset: DF.Check
target_item_code: DF.Link | None target_item_code: DF.Link | None
target_qty: DF.Float
target_serial_no: DF.SmallText | None
title: DF.Data | None title: DF.Data | None
total_value: DF.Currency total_value: DF.Currency
# end: auto-generated types # end: auto-generated types
@@ -190,22 +182,13 @@ class AssetCapitalization(StockController):
if not target_item.is_fixed_asset: if not target_item.is_fixed_asset:
frappe.throw(_("Target Item {0} must be a Fixed Asset item").format(target_item.name)) frappe.throw(_("Target Item {0} must be a Fixed Asset item").format(target_item.name))
if target_item.is_fixed_asset:
self.target_qty = 1
if flt(self.target_qty) <= 0:
frappe.throw(_("Target Qty must be a positive number"))
if not target_item.has_batch_no:
self.target_batch_no = None
if not target_item.has_serial_no:
self.target_serial_no = ""
self.validate_item(target_item) self.validate_item(target_item)
def validate_target_asset(self): def validate_target_asset(self):
if self.target_asset: if self.target_asset:
target_asset = self.get_asset_for_validation(self.target_asset) target_asset = self.get_asset_for_validation(self.target_asset)
if not target_asset.is_composite_asset: if not target_asset.asset_type == "Composite Asset":
frappe.throw(_("Target Asset {0} needs to be composite asset").format(target_asset.name)) frappe.throw(_("Target Asset {0} needs to be composite asset").format(target_asset.name))
if target_asset.item_code != self.target_item_code: if target_asset.item_code != self.target_item_code:
@@ -314,7 +297,7 @@ class AssetCapitalization(StockController):
return frappe.db.get_value( return frappe.db.get_value(
"Asset", "Asset",
asset, asset,
["name", "item_code", "company", "status", "docstatus", "is_composite_asset"], ["name", "item_code", "company", "status", "docstatus", "asset_type"],
as_dict=1, as_dict=1,
) )
@@ -380,8 +363,7 @@ class AssetCapitalization(StockController):
self.total_value = self.stock_items_total + self.asset_items_total + self.service_items_total self.total_value = self.stock_items_total + self.asset_items_total + self.service_items_total
self.total_value = flt(self.total_value, self.precision("total_value")) self.total_value = flt(self.total_value, self.precision("total_value"))
self.target_qty = flt(self.target_qty, self.precision("target_qty")) self.target_incoming_rate = self.total_value
self.target_incoming_rate = self.total_value / self.target_qty
def update_stock_ledger(self): def update_stock_ledger(self):
sl_entries = [] sl_entries = []
@@ -489,7 +471,7 @@ class AssetCapitalization(StockController):
for item in self.asset_items: for item in self.asset_items:
asset = frappe.get_doc("Asset", item.asset) asset = frappe.get_doc("Asset", item.asset)
if not asset.is_composite_component: if asset.asset_type != "Composite Component":
if asset.calculate_depreciation: if asset.calculate_depreciation:
notes = _( notes = _(
"This schedule was created when Asset {0} was consumed through Asset Capitalization {1}." "This schedule was created when Asset {0} was consumed through Asset Capitalization {1}."
@@ -542,30 +524,29 @@ class AssetCapitalization(StockController):
def get_composite_component_value(self): def get_composite_component_value(self):
composite_component_value = 0 composite_component_value = 0
for item in self.asset_items: for item in self.asset_items:
asset = frappe.db.get_value("Asset", item.asset, ["is_composite_component"], as_dict=True) asset = frappe.db.get_value("Asset", item.asset, ["asset_type"], as_dict=True)
if asset and asset.is_composite_component: if asset and asset.asset_type == "Composite Component":
composite_component_value += flt(item.asset_value, item.precision("asset_value")) composite_component_value += flt(item.asset_value, item.precision("asset_value"))
return composite_component_value return composite_component_value
def get_gl_entries_for_target_item( def get_gl_entries_for_target_item(
self, gl_entries, target_account, target_against, precision, composite_component_value self, gl_entries, target_account, target_against, precision, composite_component_value
): ):
if self.target_is_fixed_asset: total_value = flt(self.total_value - composite_component_value, precision)
total_value = flt(self.total_value - composite_component_value, precision) if total_value:
if total_value: # Capitalization
# Capitalization gl_entries.append(
gl_entries.append( self.get_gl_dict(
self.get_gl_dict( {
{ "account": target_account,
"account": target_account, "against": ", ".join(target_against),
"against": ", ".join(target_against), "remarks": self.get("remarks") or _("Accounting Entry for Asset"),
"remarks": self.get("remarks") or _("Accounting Entry for Asset"), "debit": total_value,
"debit": total_value, "cost_center": self.get("cost_center"),
"cost_center": self.get("cost_center"), },
}, item=self,
item=self,
)
) )
)
def update_target_asset(self): def update_target_asset(self):
total_target_asset_value = flt(self.total_value, self.precision("total_value")) total_target_asset_value = flt(self.total_value, self.precision("total_value"))
@@ -611,14 +592,13 @@ class AssetCapitalization(StockController):
def set_consumed_asset_status(self, asset): def set_consumed_asset_status(self, asset):
if self.docstatus == 1: if self.docstatus == 1:
if self.target_is_fixed_asset: asset.set_status("Capitalized")
asset.set_status("Capitalized") add_asset_activity(
add_asset_activity( asset.name,
asset.name, _("Asset capitalized after Asset Capitalization {0} was submitted").format(
_("Asset capitalized after Asset Capitalization {0} was submitted").format( get_link_to_form("Asset Capitalization", self.name)
get_link_to_form("Asset Capitalization", self.name) ),
), )
)
else: else:
asset.set_status() asset.set_status()
add_asset_activity( add_asset_activity(
@@ -630,7 +610,7 @@ class AssetCapitalization(StockController):
@frappe.whitelist() @frappe.whitelist()
def get_target_item_details(item_code=None, company=None): def get_target_item_details(item_code: str | None = None, company: str | None = None) -> frappe._dict:
out = frappe._dict() out = frappe._dict()
# Get Item Details # Get Item Details
@@ -640,17 +620,6 @@ def get_target_item_details(item_code=None, company=None):
# Set Item Details # Set Item Details
out.target_item_name = item.item_name out.target_item_name = item.item_name
out.target_is_fixed_asset = cint(item.is_fixed_asset)
out.target_has_batch_no = cint(item.has_batch_no)
out.target_has_serial_no = cint(item.has_serial_no)
if out.target_is_fixed_asset:
out.target_qty = 1
if not out.target_has_batch_no:
out.target_batch_no = None
if not out.target_has_serial_no:
out.target_serial_no = ""
# Cost Center # Cost Center
item_defaults = get_item_defaults(item.name, company) item_defaults = get_item_defaults(item.name, company)
@@ -667,7 +636,7 @@ def get_target_item_details(item_code=None, company=None):
@frappe.whitelist() @frappe.whitelist()
def get_target_asset_details(asset=None, company=None): def get_target_asset_details(asset: str | None = None, company: str | None = None) -> frappe._dict:
out = frappe._dict() out = frappe._dict()
# Get Asset Details # Get Asset Details

View File

@@ -10,12 +10,14 @@ from erpnext.assets.doctype.asset.depreciation import post_depreciation_entries
from erpnext.assets.doctype.asset.test_asset import ( from erpnext.assets.doctype.asset.test_asset import (
create_asset, create_asset,
create_asset_data, create_asset_data,
create_fixed_asset_item,
set_depreciation_settings_in_company, set_depreciation_settings_in_company,
) )
from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_schedule import ( from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_schedule import (
get_asset_depr_schedule_doc, get_asset_depr_schedule_doc,
) )
from erpnext.stock.doctype.item.test_item import create_item from erpnext.stock.doctype.item.test_item import create_item
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
from erpnext.stock.doctype.serial_and_batch_bundle.test_serial_and_batch_bundle import ( from erpnext.stock.doctype.serial_and_batch_bundle.test_serial_and_batch_bundle import (
make_serial_batch_bundle, make_serial_batch_bundle,
) )
@@ -61,7 +63,7 @@ class TestAssetCapitalization(IntegrationTestCase):
wip_composite_asset = create_asset( wip_composite_asset = create_asset(
asset_name="Asset Capitalization WIP Composite Asset", asset_name="Asset Capitalization WIP Composite Asset",
is_composite_asset=1, asset_type="Composite Asset",
warehouse="Stores - TCP1", warehouse="Stores - TCP1",
company=company, company=company,
) )
@@ -81,7 +83,6 @@ class TestAssetCapitalization(IntegrationTestCase):
) )
# Test Asset Capitalization values # Test Asset Capitalization values
self.assertEqual(asset_capitalization.target_qty, 1)
self.assertEqual(asset_capitalization.stock_items[0].valuation_rate, stock_rate) self.assertEqual(asset_capitalization.stock_items[0].valuation_rate, stock_rate)
self.assertEqual(asset_capitalization.stock_items[0].amount, stock_amount) self.assertEqual(asset_capitalization.stock_items[0].amount, stock_amount)
@@ -156,7 +157,7 @@ class TestAssetCapitalization(IntegrationTestCase):
wip_composite_asset = create_asset( wip_composite_asset = create_asset(
asset_name="Asset Capitalization WIP Composite Asset", asset_name="Asset Capitalization WIP Composite Asset",
is_composite_asset=1, asset_type="Composite Asset",
warehouse="Stores - TCP1", warehouse="Stores - TCP1",
company=company, company=company,
) )
@@ -176,8 +177,6 @@ class TestAssetCapitalization(IntegrationTestCase):
) )
# Test Asset Capitalization values # Test Asset Capitalization values
self.assertEqual(asset_capitalization.target_qty, 1)
self.assertEqual(asset_capitalization.stock_items[0].valuation_rate, stock_rate) self.assertEqual(asset_capitalization.stock_items[0].valuation_rate, stock_rate)
self.assertEqual(asset_capitalization.stock_items[0].amount, stock_amount) self.assertEqual(asset_capitalization.stock_items[0].amount, stock_amount)
self.assertEqual(asset_capitalization.stock_items_total, stock_amount) self.assertEqual(asset_capitalization.stock_items_total, stock_amount)
@@ -245,7 +244,7 @@ class TestAssetCapitalization(IntegrationTestCase):
wip_composite_asset = create_asset( wip_composite_asset = create_asset(
asset_name="Asset Capitalization WIP Composite Asset", asset_name="Asset Capitalization WIP Composite Asset",
is_composite_asset=1, asset_type="Composite Asset",
warehouse="Stores - TCP1", warehouse="Stores - TCP1",
company=company, company=company,
) )
@@ -262,8 +261,6 @@ class TestAssetCapitalization(IntegrationTestCase):
) )
# Test Asset Capitalization values # Test Asset Capitalization values
self.assertEqual(asset_capitalization.target_qty, 1)
self.assertEqual(asset_capitalization.stock_items[0].valuation_rate, stock_rate) self.assertEqual(asset_capitalization.stock_items[0].valuation_rate, stock_rate)
self.assertEqual(asset_capitalization.stock_items[0].amount, stock_amount) self.assertEqual(asset_capitalization.stock_items[0].amount, stock_amount)
self.assertEqual(asset_capitalization.stock_items_total, stock_amount) self.assertEqual(asset_capitalization.stock_items_total, stock_amount)
@@ -313,7 +310,7 @@ class TestAssetCapitalization(IntegrationTestCase):
wip_composite_asset = create_asset( wip_composite_asset = create_asset(
asset_name="Asset Capitalization WIP Composite Asset", asset_name="Asset Capitalization WIP Composite Asset",
is_composite_asset=1, asset_type="Composite Asset",
warehouse="Stores - TCP1", warehouse="Stores - TCP1",
company=company, company=company,
) )
@@ -361,33 +358,45 @@ class TestAssetCapitalization(IntegrationTestCase):
wip_composite_asset = create_asset( wip_composite_asset = create_asset(
asset_name="Asset Capitalization WIP Composite Asset", asset_name="Asset Capitalization WIP Composite Asset",
is_composite_asset=1, asset_type="Composite Asset",
warehouse="Stores - TCP1", warehouse="Stores - TCP1",
company=company, company=company,
) )
consumed_asset_value = 100000 consumed_asset_value = 100000
consumed_asset = create_asset( item = create_fixed_asset_item("Asset Capitalization Consumable Asset")
asset_name="Asset Capitalization Consumable Asset",
asset_value=consumed_asset_value, pr = make_purchase_receipt(
submit=1, item_code=item.item_code,
warehouse="Stores - _TC", qty=1,
is_composite_component=1, rate=consumed_asset_value,
company=company, company=company,
warehouse="Stores - TCP1",
) )
consumed_asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, "name")
consumed_asset_doc = frappe.get_doc("Asset", consumed_asset_name)
consumed_asset_doc.update(
{
"asset_type": "Composite Component",
"purchase_date": pr.posting_date,
"available_for_use_date": pr.posting_date,
}
)
consumed_asset_doc.save()
consumed_asset_doc.submit()
# Create and submit Asset Captitalization # Create and submit Asset Captitalization
asset_capitalization = create_asset_capitalization( asset_capitalization = create_asset_capitalization(
target_asset=wip_composite_asset.name, target_asset=wip_composite_asset.name,
target_asset_location="Test Location", target_asset_location="Test Location",
consumed_asset=consumed_asset.name, consumed_asset=consumed_asset_doc.name,
company=company, company=company,
submit=1, submit=1,
) )
# Test Asset Capitalization values # Test Asset Capitalization values
self.assertEqual(asset_capitalization.target_qty, 1)
self.assertEqual(asset_capitalization.asset_items[0].asset_value, consumed_asset_value) self.assertEqual(asset_capitalization.asset_items[0].asset_value, consumed_asset_value)
actual_gle = get_actual_gle_dict(asset_capitalization.name) actual_gle = get_actual_gle_dict(asset_capitalization.name)
@@ -421,9 +430,6 @@ def create_asset_capitalization(**args):
"target_item_code": target_item_code, "target_item_code": target_item_code,
"target_asset": target_asset.name, "target_asset": target_asset.name,
"target_asset_location": "Test Location", "target_asset_location": "Test Location",
"target_qty": flt(args.target_qty) or 1,
"target_batch_no": args.target_batch_no,
"target_serial_no": args.target_serial_no,
"finance_book": args.finance_book, "finance_book": args.finance_book,
} }
) )
@@ -516,7 +522,7 @@ def create_depreciation_asset(**args):
args = frappe._dict(args) args = frappe._dict(args)
asset = frappe.new_doc("Asset") asset = frappe.new_doc("Asset")
asset.is_existing_asset = 1 asset.asset_type = args.asset_type or "Existing Asset"
asset.calculate_depreciation = 1 asset.calculate_depreciation = 1
asset.asset_owner = "Company" asset.asset_owner = "Company"

View File

@@ -87,7 +87,7 @@ class TestAssetDepreciationSchedule(IntegrationTestCase):
calculate_depreciation=1, calculate_depreciation=1,
depreciation_method="Straight Line", depreciation_method="Straight Line",
available_for_use_date="2023-10-10", available_for_use_date="2023-10-10",
is_existing_asset=1, asset_type="Existing Asset",
opening_number_of_booked_depreciations=9, opening_number_of_booked_depreciations=9,
opening_accumulated_depreciation=265, opening_accumulated_depreciation=265,
depreciation_start_date="2024-07-31", depreciation_start_date="2024-07-31",
@@ -127,7 +127,7 @@ class TestAssetDepreciationSchedule(IntegrationTestCase):
calculate_depreciation=1, calculate_depreciation=1,
depreciation_method="Straight Line", depreciation_method="Straight Line",
available_for_use_date="2023-10-10", available_for_use_date="2023-10-10",
is_existing_asset=1, asset_type="Existing Asset",
opening_number_of_booked_depreciations=9, opening_number_of_booked_depreciations=9,
opening_accumulated_depreciation=265.30, opening_accumulated_depreciation=265.30,
depreciation_start_date="2024-07-31", depreciation_start_date="2024-07-31",
@@ -165,7 +165,7 @@ class TestAssetDepreciationSchedule(IntegrationTestCase):
calculate_depreciation=1, calculate_depreciation=1,
depreciation_method="Straight Line", depreciation_method="Straight Line",
available_for_use_date="2023-11-01", available_for_use_date="2023-11-01",
is_existing_asset=1, asset_type="Existing Asset",
opening_number_of_booked_depreciations=4, opening_number_of_booked_depreciations=4,
opening_accumulated_depreciation=223.15, opening_accumulated_depreciation=223.15,
depreciation_start_date="2024-12-31", depreciation_start_date="2024-12-31",
@@ -529,7 +529,7 @@ class TestAssetDepreciationSchedule(IntegrationTestCase):
depreciation_start_date="2023-03-31", depreciation_start_date="2023-03-31",
frequency_of_depreciation=1, frequency_of_depreciation=1,
total_number_of_depreciations=12, total_number_of_depreciations=12,
is_existing_asset=1, asset_type="Existing Asset",
opening_accumulated_depreciation=64.52, opening_accumulated_depreciation=64.52,
opening_number_of_booked_depreciations=2, opening_number_of_booked_depreciations=2,
submit=1, submit=1,
@@ -851,7 +851,7 @@ class TestAssetDepreciationSchedule(IntegrationTestCase):
depreciation_start_date="2023-03-31", depreciation_start_date="2023-03-31",
frequency_of_depreciation=1, frequency_of_depreciation=1,
total_number_of_depreciations=12, total_number_of_depreciations=12,
is_existing_asset=1, asset_type="Existing Asset",
opening_accumulated_depreciation=64.52, opening_accumulated_depreciation=64.52,
opening_number_of_booked_depreciations=2, opening_number_of_booked_depreciations=2,
submit=1, submit=1,
@@ -925,7 +925,7 @@ class TestAssetDepreciationSchedule(IntegrationTestCase):
depreciation_start_date="2021-12-31", depreciation_start_date="2021-12-31",
frequency_of_depreciation=12, frequency_of_depreciation=12,
total_number_of_depreciations=3, total_number_of_depreciations=3,
is_existing_asset=1, asset_type="Existing Asset",
submit=1, submit=1,
) )
post_depreciation_entries(date="2021-12-31") post_depreciation_entries(date="2021-12-31")
@@ -1014,7 +1014,7 @@ class TestAssetDepreciationSchedule(IntegrationTestCase):
depreciation_start_date="2021-12-31", depreciation_start_date="2021-12-31",
frequency_of_depreciation=12, frequency_of_depreciation=12,
total_number_of_depreciations=3, total_number_of_depreciations=3,
is_existing_asset=1, asset_type="Existing Asset",
submit=1, submit=1,
) )
post_depreciation_entries(date="2021-12-31") post_depreciation_entries(date="2021-12-31")
@@ -1093,7 +1093,7 @@ class TestAssetDepreciationSchedule(IntegrationTestCase):
rate_of_depreciation=50, rate_of_depreciation=50,
frequency_of_depreciation=12, frequency_of_depreciation=12,
total_number_of_depreciations=3, total_number_of_depreciations=3,
is_existing_asset=1, asset_type="Existing Asset",
submit=1, submit=1,
) )
post_depreciation_entries(date="2021-12-31") post_depreciation_entries(date="2021-12-31")

View File

@@ -9,9 +9,9 @@
"engine": "InnoDB", "engine": "InnoDB",
"field_order": [ "field_order": [
"naming_series", "naming_series",
"company",
"asset", "asset",
"asset_name", "asset_name",
"company",
"column_break_2", "column_break_2",
"repair_status", "repair_status",
"failure_date", "failure_date",
@@ -28,10 +28,6 @@
"column_break_ajbh", "column_break_ajbh",
"column_break_hkem", "column_break_hkem",
"repair_cost", "repair_cost",
"accounting_dimensions_section",
"cost_center",
"column_break_14",
"project",
"stock_consumption_details_section", "stock_consumption_details_section",
"stock_items", "stock_items",
"section_break_ltbb", "section_break_ltbb",
@@ -43,7 +39,12 @@
"capitalize_repair_cost", "capitalize_repair_cost",
"increase_in_asset_life", "increase_in_asset_life",
"column_break_xebe", "column_break_xebe",
"total_repair_cost" "total_repair_cost",
"accounting_dimensions_section",
"cost_center",
"column_break_14",
"project",
"connection_tab"
], ],
"fields": [ "fields": [
{ {
@@ -149,8 +150,7 @@
{ {
"fieldname": "accounting_details", "fieldname": "accounting_details",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hide_border": 1, "hide_border": 1
"label": "Repair Purchase Invoices"
}, },
{ {
"fieldname": "stock_items", "fieldname": "stock_items",
@@ -206,6 +206,7 @@
{ {
"fieldname": "invoices", "fieldname": "invoices",
"fieldtype": "Table", "fieldtype": "Table",
"label": "Repair Purchase Invoices",
"mandatory_depends_on": "eval: doc.repair_status == 'Completed' && doc.repair_cost > 0;", "mandatory_depends_on": "eval: doc.repair_status == 'Completed' && doc.repair_cost > 0;",
"no_copy": 1, "no_copy": 1,
"options": "Asset Repair Purchase Invoice" "options": "Asset Repair Purchase Invoice"
@@ -244,6 +245,7 @@
"fieldtype": "Column Break" "fieldtype": "Column Break"
}, },
{ {
"depends_on": "eval: doc.consumed_items_cost",
"fieldname": "consumed_items_cost", "fieldname": "consumed_items_cost",
"fieldtype": "Currency", "fieldtype": "Currency",
"label": "Consumed Items Cost" "label": "Consumed Items Cost"
@@ -256,7 +258,13 @@
"depends_on": "capitalize_repair_cost", "depends_on": "capitalize_repair_cost",
"fieldname": "accounting_dimensions_section", "fieldname": "accounting_dimensions_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Accounting Dimensions" "label": "Accounting Dimension"
},
{
"fieldname": "connection_tab",
"fieldtype": "Tab Break",
"label": "Connection",
"show_dashboard": 1
} }
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
@@ -267,7 +275,7 @@
"link_fieldname": "asset_repair" "link_fieldname": "asset_repair"
} }
], ],
"modified": "2026-01-06 15:48:13.862505", "modified": "2026-02-06 14:57:54.257572",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Assets", "module": "Assets",
"name": "Asset Repair", "name": "Asset Repair",

View File

@@ -360,7 +360,7 @@ class TestAssetRepair(IntegrationTestCase):
self.assertEqual(stock_entry.asset_repair, asset_repair.name) self.assertEqual(stock_entry.asset_repair, asset_repair.name)
def test_gl_entries_with_capitalized_asset_repair(self): def test_gl_entries_with_capitalized_asset_repair(self):
asset = create_asset(is_existing_asset=1, calculate_depreciation=1, submit=1) asset = create_asset(asset_type="Existing Asset", calculate_depreciation=1, submit=1)
asset_repair = create_asset_repair( asset_repair = create_asset_repair(
asset=asset, capitalize_repair_cost=1, item="_Test Non Stock Item", submit=1 asset=asset, capitalize_repair_cost=1, item="_Test Non Stock Item", submit=1
) )
@@ -400,7 +400,7 @@ def create_asset_repair(**args):
if args.asset: if args.asset:
asset = args.asset asset = args.asset
else: else:
asset = create_asset(is_existing_asset=1, submit=1, company=args.company) asset = create_asset(asset_type=args.asset_type or "Existing Asset", submit=1, company=args.company)
asset_repair = frappe.new_doc("Asset Repair") asset_repair = frappe.new_doc("Asset Repair")
asset_repair.update( asset_repair.update(
{ {

View File

@@ -144,7 +144,7 @@ def get_conditions(filters):
conditions[date_field] = ["between", [filters.year_start_date, filters.year_end_date]] conditions[date_field] = ["between", [filters.year_start_date, filters.year_end_date]]
if filters.get("only_existing_assets"): if filters.get("only_existing_assets"):
conditions["is_existing_asset"] = filters.get("only_existing_assets") conditions["asset_type"] = "Existing Asset"
if filters.get("asset_category"): if filters.get("asset_category"):
conditions["asset_category"] = filters.get("asset_category") conditions["asset_category"] = filters.get("asset_category")
if filters.get("cost_center"): if filters.get("cost_center"):
@@ -274,7 +274,7 @@ def get_asset_depreciation_amount_map(filters, finance_book):
) )
if filters.only_existing_assets: if filters.only_existing_assets:
query = query.where(asset.is_existing_asset == 1) query = query.where(asset.asset_type == "Existing Asset")
if filters.asset_category: if filters.asset_category:
query = query.where(asset.asset_category == filters.asset_category) query = query.where(asset.asset_category == filters.asset_category)
if filters.cost_center: if filters.cost_center:
@@ -325,7 +325,7 @@ def get_asset_value_adjustment_map(filters, finance_book):
) )
if filters.only_existing_assets: if filters.only_existing_assets:
query = query.where(asset.is_existing_asset == 1) query = query.where(asset.asset_type == "Existing Asset")
if filters.asset_category: if filters.asset_category:
query = query.where(asset.asset_category == filters.asset_category) query = query.where(asset.asset_category == filters.asset_category)
if filters.cost_center: if filters.cost_center:

View File

@@ -468,5 +468,6 @@ erpnext.patches.v15_0.replace_http_with_https_in_sales_partner
erpnext.patches.v15_0.delete_quotation_lost_record_detail erpnext.patches.v15_0.delete_quotation_lost_record_detail
erpnext.patches.v16_0.add_portal_redirects erpnext.patches.v16_0.add_portal_redirects
erpnext.patches.v16_0.complete_onboarding_steps_for_older_sites #2 erpnext.patches.v16_0.complete_onboarding_steps_for_older_sites #2
erpnext.patches.v16_0.migrate_asset_type_checkboxes_to_select
erpnext.patches.v16_0.update_order_qty_and_requested_qty_based_on_mr_and_po erpnext.patches.v16_0.update_order_qty_and_requested_qty_based_on_mr_and_po
erpnext.patches.v16_0.enable_serial_batch_setting erpnext.patches.v16_0.enable_serial_batch_setting

View File

@@ -0,0 +1,25 @@
import frappe
from frappe.query_builder import Case
def execute():
required_columns = [
"is_existing_asset",
"is_composite_asset",
"is_composite_component",
]
# Skip patch if any required column is missing
if not all(frappe.db.has_column("Asset", col) for col in required_columns):
return
Asset = frappe.qb.DocType("Asset")
frappe.qb.update(Asset).set(
Asset.asset_type,
Case()
.when(Asset.is_existing_asset == 1, "Existing Asset")
.when(Asset.is_composite_asset == 1, "Composite Asset")
.when(Asset.is_composite_component == 1, "Composite Component")
.else_(""),
).run()